package io.gatling.extensions.redis.action

import java.util.{HashMap => JHashMap}

import com.typesafe.scalalogging.StrictLogging
import io.gatling.commons.stats.{KO, OK}
import io.gatling.commons.util.Clock
import io.gatling.commons.validation
import io.gatling.core.CoreComponents
import io.gatling.core.action.{Action, ExitableAction}
import io.gatling.core.check.Check.PreparedCache
import io.gatling.core.check._
import io.gatling.core.session.{Expression, Session}
import io.gatling.core.stats.StatsEngine
import io.gatling.core.util.NameGen
import io.gatling.extensions.redis.protocol.RedisComponents
import redis.clients.jedis.Jedis


// 注意：Action对象是唯一的，在不同的session中执行的是同一个Action，所以请将连接放在其他地方，如ProtocolComponents中（ProtocolComponents每个session都有一个）：
class RedisGetAction[R](
                      key: Expression[String],
                      coreComponents: CoreComponents,
                      val redisComponents: RedisComponents,
                      throttled: Boolean,
                      val checks: List[Check[String]],
                      val next: Action
                    ) extends ExitableAction with NameGen with StrictLogging {

  import coreComponents._

  // 用于上报执行结果，供DataWriterData的各种实现使用
  override def statsEngine: StatsEngine = coreComponents.statsEngine

  // 用于获取action开始和结束时间，效果类似于System.currentTimeMillis()
  override def clock: Clock = coreComponents.clock

  override def name: String = genName("redis-get-action")

  val preparedCache: PreparedCache = Check.newPreparedCache

  // 真正执行的方法
  override def execute(session: Session): Unit = {

    // key类型是Expression[String]，本质是一个Session => Validation[T]函数
    // 这样处理之后会将key中的EL表达式转化为对应的数据，如${user_id}等写法
    key(session) map {

      keyS => {
        // 记录启动时间
        val requestStartDate = clock.nowMillis
        // 获取当前session的连接对象（此处根据需求实现：可以重新建立连接，也可以获取已存在的连接）
        val jedis: Jedis = redisComponents.jedisMap(session.userId)

        // 应尽量避免阻塞，执行逻辑应当异步处理，此处的写法有问题，只是一个示例
        val str: String = jedis.get(keyS)

        // 执行check，此处使用gatling自带的actorSystem，进行异步操作（为什么要异步？？？）
        coreComponents.actorSystem.dispatcher.execute(() => {

          val checkRes: (Session, Option[validation.Failure]) = Check.check(str, session, checks, preparedCache)

          // 检查之后的处理逻辑
          checkRes._2 match {
            case Some(failure) => statsEngine.logResponse(
              session.scenario,
              session.groups,
              keyS,
              requestStartDate,
              clock.nowMillis,
              KO,
              Some(failure.message),
              Some(failure.message)
            )
            case None => statsEngine.logResponse(
              session.scenario,
              session.groups,
              keyS,
              requestStartDate,
              clock.nowMillis,
              OK,
              Some(str),
              Some(str)
            )
          }

          throttler match {
            case Some(th) if throttled => th.throttle(session.scenario, () => next ! session)
            case None => next ! session
          }
        })
      }
    }
  }
}
